// use anyhow::Result;
use std::collections::HashMap;
use std::fs::{File, OpenOptions};
use std::io::ErrorKind;
use std::io::Read;
use std::io::{BufRead, BufReader, Write};

use crate::types; //or use super::types;

pub fn file_demo() {
    println!("");
    println!("read_file_vec start");
    let book_vec = read_file_vec();
    let book_vec = match book_vec {
        Ok(rows) => rows,
        Err(err) => {
            println!("read_file_vec err:{:?}", err);
            return;
        }
    };

    println!("read_file_vec books len:{}", book_vec.len());
    for book in book_vec {
        println!("read_file_vec book:{:?}", book);
    }

    println!("");
    println!("read_file_map start");
    let book_map = read_file_map();
    let book_map = match book_map {
        Ok(rows) => rows,
        Err(err) => {
            println!("read_file_map err:{:?}", err);
            return;
        }
    };

    println!("read_file_map books len:{}", book_map.len());
    for (id, book) in book_map {
        println!("read_file_map id:{}, book:{:?}", id, book);
    }

    println!("");
    println!("read_or_create start");
    read_or_create();

    println!("");
    println!("read_or_create2 start");
    read_or_create2().unwrap();

    println!("");
    println!("read_or_create3 start");
    read_or_create3().unwrap();

    println!("");
    println!("read_or_create4 start");
    read_or_create4().unwrap();
}

fn read_file_vec() -> Result<Vec<types::Book>, std::io::Error> {
    let file = File::open("src/test.txt").expect("open test.txt failed");
    let buf = BufReader::new(file);

    let mut index = 0;
    let mut books: Vec<types::Book> = Vec::new();
    for row in buf.lines() {
        println!("test.txt[{}]={:?}", index, row);
        index += 1;

        match row {
            Ok(line) => {
                let strs: Vec<&str> = line.split(",").collect();
                let id: u64 = strs[0].parse().unwrap();
                books.push(types::Book::new(id, strs[1], types::BookType::Rust));
            }
            Err(err) => {
                return Err(err);
            }
        }
    }

    Ok(books)
}

fn read_file_map() -> Result<HashMap<u64, types::Book>, std::io::Error> {
    let file = File::open("src/test.txt").expect("open test.txt failed");
    let buf = BufReader::new(file);

    let mut index = 0;
    let mut books: HashMap<u64, types::Book> = HashMap::new();
    for row in buf.lines() {
        println!("test.txt[{}]={:?}", index, row);
        index += 1;

        let line = match row {
            Ok(line) => line,
            Err(err) => {
                return Err(err);
            }
        };

        let strs: Vec<&str> = line.split(",").collect();
        let id: u64 = strs[0].parse().unwrap();
        let book = types::Book::new(id, strs[1], types::BookType::Rust);
        books.insert(id, book);
    }

    Ok(books)
}

fn read_or_create() {
    let ret = File::open("src/test.txt")
        .map_err(|error| {
            if error.kind() == ErrorKind::NotFound {
                File::create("src/test.txt").unwrap_or_else(|error| {
                    panic!("create file failed: {:?}", error);
                })
            } else {
                panic!("open file failed: {:?}", error);
            }
        })
        .and_then(|mut file| {
            let mut content = String::new();
            file.read_to_string(&mut content)
                .map_err(|error| {
                    panic!("file read_to_string failed: {:?}", error);
                })
                .map(|_| content)
        });

    let content = match ret {
        Ok(content) => content,
        Err(err) => {
            panic!("read_or_create failed: {:?}", err)
        }
    };

    println!("read_or_create content:{:?}", content);
}

fn read_or_create2() -> Result<(), types::ReadFileError> {
    let path = "src/test1.txt";
    let ret = File::open(path);
    let mut file = match ret {
        Ok(file) => file,
        Err(err) => {
            println!("read_or_create2 open failed, err.kind:{:?}", err.kind());
            match err.kind() {
                ErrorKind::NotFound => {
                    println!("read_or_create2 create file start");

                    let fcr = File::create(path);
                    match fcr {
                        Ok(fc) => fc,
                        Err(err) => {
                            println!("read_or_create2 create failed:{:?}", err);
                            return Err(types::ReadFileError::CreateFailed);
                        }
                    }
                }
                other_error => {
                    println!("read_or_create2 open failed, other_error:{:?}", other_error);
                    return Err(types::ReadFileError::Unknown);
                }
            }
        }
    };

    let mut content = String::new();
    file.read_to_string(&mut content)?;

    println!("read_or_create2 content:{:?}", content);
    Ok(())
}

fn read_or_create3() -> Result<(), types::ReadFileError> {
    let path = "src/test1.txt";
    let _ = File::open(path)
        .map_err(|err| {
            println!("read_or_create3 open failed, err.kind:{:?}", err.kind());
            let ret = match err.kind() {
                ErrorKind::NotFound => {
                    println!("read_or_create3 create file start");

                    let fcr = File::create(path);
                    match fcr {
                        Ok(fc) => fc,
                        Err(err) => {
                            println!("read_or_create3 create failed:{:?}", err);
                            return Err(types::ReadFileError::CreateFailed);
                        }
                    }
                }
                other_error => {
                    println!("read_or_create3 open failed, other_error:{:?}", other_error);
                    return Err(types::ReadFileError::OpenFailed);
                }
            };
            Ok(ret)
        })
        .and_then(|mut file| {
            let mut content = String::new();
            file.read_to_string(&mut content)
                .map_err(|err| {
                    println!("read_or_create3 read_to_string failed:{:?}", err);
                    return Err(types::ReadFileError::ReadFailed);
                })
                .and_then(|ok| {
                    println!("read_or_create3 content:{:?}, ok:{:?}", content, ok);
                    Ok(())
                })
        });

    Ok(())
}

fn read_or_create4() -> Result<(), types::ReadFileError> {
    let path = "src/test2.txt";
    let mut file = match File::open(path) {
        Ok(file) => file,
        Err(err) if err.kind() == ErrorKind::NotFound => {
            println!(
                "read_or_create4 open failed, err.kind:{:?}, create start",
                err.kind()
            );
            let mut file = match File::create(path) {
                Err(err) => {
                    println!("read_or_create4, create file failed:{:?}", err);
                    return Err(types::ReadFileError::CreateFailed);
                }
                Ok(file) => file,
            };
            file.write("aaa\n".as_bytes())?;
            file.flush()?;
            OpenOptions::new()
                .read(true)
                .write(true)
                .append(true)
                .open(path)?
        }
        Err(err) => {
            println!("read_or_create4, open file failed:{:?}", err);
            return Err(types::ReadFileError::Unknown);
        }
    };

    let mut content = String::new();
    file.read_to_string(&mut content)?;

    for line in content.lines() {
        println!("read_or_create4 line:{:?}", line);
    }

    Ok(())
}
